#include "flash_board.h"
#include "real_time_app.h"
#include "user_wwdg.h"

void FlashEraseBlock(uint32_t EraseStartAddr, uint32_t EraseSize)
{
  FLASH_ROM_ERASE(EraseStartAddr, EraseSize);
}

void FlashReadCacheData(uint32_t ReadStartAddr, void *pDataOut, uint32_t SizeOut)
{
  if ((pDataOut != NULL) && (SizeOut != 0))
  {
    FLASH_ROM_READ(ReadStartAddr, pDataOut, SizeOut);
  }
}

bool FlashWriteCacheDataToCheak(uint32_t WriteStartAddr, const void *pDataIn, uint32_t SizeIn)
{
  bool WriteDataResult = false;
  uint8_t NumberOfMatchingFailures = 0, *pSectorDataBuff = NULL;
  uint32_t SectorAddressOffset = 0, SectorMemoryOffset = 0, SectorBuffOffset = 0;

  if (pDataIn != NULL)
  {
    if ((SizeIn > 0) && ((WriteStartAddr + SizeIn) < FLASH_MAX_OPERATION_LENGTH))
    {
      pSectorDataBuff = tmos_msg_allocate(FLASH_MAX_SECTOR_SIZE);
      if (pSectorDataBuff != NULL)
      {
        while (1)
        {
          SectorAddressOffset = WriteStartAddr / FLASH_MAX_SECTOR_SIZE;
          SectorMemoryOffset = WriteStartAddr % FLASH_MAX_SECTOR_SIZE;
          SectorBuffOffset = FLASH_MAX_SECTOR_SIZE - SectorMemoryOffset;

          if (SectorBuffOffset > SizeIn)
          {
            SectorBuffOffset = SizeIn;
          }

          FLASH_ROM_READ(SectorAddressOffset * FLASH_MAX_SECTOR_SIZE, pSectorDataBuff, FLASH_MAX_SECTOR_SIZE);
          for (uint32_t i = 0; i < FLASH_MAX_SECTOR_SIZE; i++)
          {
            if (pSectorDataBuff[i] != 0xFF)
            {
              FlashEraseBlock(SectorAddressOffset * FLASH_MAX_SECTOR_SIZE, FLASH_MAX_SECTOR_SIZE);
              break;
            }
          }

          memcpy(&pSectorDataBuff[SectorMemoryOffset], pDataIn, SectorBuffOffset);
          FLASH_ROM_WRITE(SectorAddressOffset * FLASH_MAX_SECTOR_SIZE, pSectorDataBuff, FLASH_MAX_SECTOR_SIZE);

          FLASH_ROM_READ(SectorAddressOffset * FLASH_MAX_SECTOR_SIZE, pSectorDataBuff, FLASH_MAX_SECTOR_SIZE);
          if (memcmp(&pSectorDataBuff[SectorMemoryOffset], pDataIn, SectorBuffOffset) == 0)
          {
            if (SectorBuffOffset == SizeIn)
            {
              WriteDataResult = true;
              break;
            }

            WriteStartAddr += SectorBuffOffset;
            pDataIn += SectorBuffOffset;
            SizeIn -= SectorBuffOffset;
          }
          else
          {
            NumberOfMatchingFailures++;
            if (NumberOfMatchingFailures > FLASH_MAX_REWRITE_NUMBER)
              break;
          }
        }

        tmos_msg_deallocate(pSectorDataBuff);
      }
      else
      {
        WriteDataResult = false;
        PRINT("[flash]: there is not enough space. \r\n");
      }
    }
  }

  return WriteDataResult;
}

uint8_t FlashCalculateFileAccumulateCleckCode(uint32_t AddressOffset, uint32_t FilzeSize)
{
  uint8_t *pSectorDataBuff = NULL;
  uint32_t CheckCode = 0, GetOffset = 0, GetSize = 0;

  pSectorDataBuff = tmos_msg_allocate(FLASH_MAX_SECTOR_SIZE);
  if (pSectorDataBuff != NULL)
  {
    if ((FilzeSize > 0) && ((AddressOffset + FilzeSize) < FLASH_MAX_OPERATION_LENGTH))
    {
      while (FilzeSize > 0)
      {
        GetSize = (FilzeSize > FLASH_MAX_SECTOR_SIZE) ? FLASH_MAX_SECTOR_SIZE : FilzeSize;

        FLASH_ROM_READ(AddressOffset + GetOffset, pSectorDataBuff, GetSize);
        for (uint32_t i = 0; i < GetSize; i++)
        {
          CheckCode += pSectorDataBuff[i];
        }

        GetOffset += GetSize;
        FilzeSize -= GetSize;
      }
    }

    tmos_msg_deallocate(pSectorDataBuff);
  }

  return (uint8_t)(CheckCode & 0x000000FF);
}

void FlashFeedWatchdog(void)
{
  WwdgFeedWatchdog();
}

bool FlashBootloaderSectorDataMove(void)
{
  bool WriteDataResult = true;

  uint8_t *pSectorDataBuff = tmos_msg_allocate(FLASH_MAX_SECTOR_SIZE);
  if (pSectorDataBuff != NULL)
  {
    for (uint32_t i = 0; i < BOOTLOADER_SECTOR_SIZE; i++)
    {
      FlashFeedWatchdog();
      
      FLASH_ROM_ERASE(BOOTLOADER_START_SECTOR_ADDR + (i * FLASH_MAX_SECTOR_SIZE), FLASH_MAX_SECTOR_SIZE);
      EEPROM_READ(FLASH_BOOTLOADER_BACKUP_START_SECTOR_ADDR + (i * FLASH_MAX_SECTOR_SIZE), pSectorDataBuff, FLASH_MAX_SECTOR_SIZE);
      FLASH_ROM_WRITE(BOOTLOADER_START_SECTOR_ADDR + (i * FLASH_MAX_SECTOR_SIZE), pSectorDataBuff, FLASH_MAX_SECTOR_SIZE);

      EEPROM_READ(FLASH_BOOTLOADER_BACKUP_START_SECTOR_ADDR + (i * FLASH_MAX_SECTOR_SIZE), pSectorDataBuff, FLASH_MAX_SECTOR_SIZE);
      if (FLASH_ROM_VERIFY(BOOTLOADER_START_SECTOR_ADDR + (i * FLASH_MAX_SECTOR_SIZE), pSectorDataBuff, FLASH_MAX_SECTOR_SIZE) != SUCCESS)
      {
        WriteDataResult = false;
        PRINT("[flash]: verify data error. \r\n");

        break;
      }
    }

    tmos_msg_deallocate(pSectorDataBuff);
  }
  else
  {
    WriteDataResult = false;
    PRINT("[flash]: there is not enough space. \r\n");
  }

  return WriteDataResult;
}
